﻿using System;
using GeoAPI.Geometries;
using NetTopologySuite.Geometries;
using NetTopologySuite.Geometries.Utilities;

namespace NetTopologySuite.Utilities
{
	public class GeometricShapeFactory
	{
		public GeometricShapeFactory() : this(new GeometryFactory())
		{
		}

		public GeometricShapeFactory(IGeometryFactory geomFact)
		{
			this.GeomFact = geomFact;
			this.PrecModel = geomFact.PrecisionModel;
		}

		public Coordinate Base
		{
			set
			{
				this._dim.Base = value;
			}
		}

		public Coordinate Centre
		{
			set
			{
				this._dim.Centre = value;
			}
		}

		public Envelope Envelope
		{
			get
			{
				return this._dim.Envelope;
			}
			set
			{
				this._dim.Envelope = value;
			}
		}

		public int NumPoints
		{
			get
			{
				return this._nPts;
			}
			set
			{
				this._nPts = value;
			}
		}

		public double Size
		{
			set
			{
				this._dim.Size = value;
			}
		}

		public double Width
		{
			get
			{
				return this._dim.Width;
			}
			set
			{
				this._dim.Width = value;
			}
		}

		public double Height
		{
			get
			{
				return this._dim.Height;
			}
			set
			{
				this._dim.Height = value;
			}
		}

		public double Rotation
		{
			get
			{
				return this._rotationAngle;
			}
			set
			{
				this._rotationAngle = value;
			}
		}

		protected IGeometry Rotate(IGeometry geom)
		{
			if (this._rotationAngle != 0.0)
			{
				Coordinate centre = this._dim.Centre;
				AffineTransformation filter = AffineTransformation.RotationInstance(this._rotationAngle, centre.X, centre.Y);
				geom.Apply(filter);
			}
			return geom;
		}

		protected Coordinate CreateCoord(double x, double y)
		{
			Coordinate coordinate = new Coordinate(x, y);
			this.PrecModel.MakePrecise(coordinate);
			return coordinate;
		}

		protected Coordinate CreateCoordTrans(double x, double y, Coordinate trans)
		{
			return this.CreateCoord(x + trans.X, y + trans.Y);
		}

		public IPolygon CreateRectangle()
		{
			int num = 0;
			int num2 = this._nPts / 4;
			if (num2 < 1)
			{
				num2 = 1;
			}
			double num3 = this._dim.Envelope.Width / (double)num2;
			double num4 = this._dim.Envelope.Height / (double)num2;
			Coordinate[] array = new Coordinate[4 * num2 + 1];
			Envelope envelope = this._dim.Envelope;
			for (int i = 0; i < num2; i++)
			{
				double x = envelope.MinX + (double)i * num3;
				double minY = envelope.MinY;
				array[num++] = this.CreateCoord(x, minY);
			}
			for (int i = 0; i < num2; i++)
			{
				double maxX = envelope.MaxX;
				double y = envelope.MinY + (double)i * num4;
				array[num++] = this.CreateCoord(maxX, y);
			}
			for (int i = 0; i < num2; i++)
			{
				double x2 = envelope.MaxX - (double)i * num3;
				double maxY = envelope.MaxY;
				array[num++] = this.CreateCoord(x2, maxY);
			}
			for (int i = 0; i < num2; i++)
			{
				double minX = envelope.MinX;
				double y2 = envelope.MaxY - (double)i * num4;
				array[num++] = this.CreateCoord(minX, y2);
			}
			array[num] = new Coordinate(array[0]);
			ILinearRing shell = this.GeomFact.CreateLinearRing(array);
			IPolygon geom = this.GeomFact.CreatePolygon(shell, null);
			return (IPolygon)this.Rotate(geom);
		}

		public IPolygon CreateCircle()
		{
			Envelope envelope = this._dim.Envelope;
			double num = envelope.Width / 2.0;
			double num2 = envelope.Height / 2.0;
			double num3 = envelope.MinX + num;
			double num4 = envelope.MinY + num2;
			Coordinate[] array = new Coordinate[this._nPts + 1];
			int num5 = 0;
			for (int i = 0; i < this._nPts; i++)
			{
				double num6 = (double)i * (System.Math.PI*2 / (double)this._nPts);
				double x = num * Math.Cos(num6) + num3;
				double y = num2 * Math.Sin(num6) + num4;
				Coordinate coordinate = this.CreateCoord(x, y);
				array[num5++] = coordinate;
			}
			array[num5] = array[0];
			ILinearRing shell = this.GeomFact.CreateLinearRing(array);
			IPolygon geom = this.GeomFact.CreatePolygon(shell, null);
			return (IPolygon)this.Rotate(geom);
		}

		public IPolygon CeateEllipse()
		{
			Envelope envelope = this._dim.Envelope;
			double num = envelope.Width / 2.0;
			double num2 = envelope.Height / 2.0;
			double num3 = envelope.MinX + num;
			double num4 = envelope.MinY + num2;
			Coordinate[] array = new Coordinate[this._nPts + 1];
			int num5 = 0;
			for (int i = 0; i < this._nPts; i++)
			{
				double num6 = (double)i * (System.Math.PI*2 / (double)this._nPts);
				double x = num * Math.Cos(num6) + num3;
				double y = num2 * Math.Sin(num6) + num4;
				array[num5++] = this.CreateCoord(x, y);
			}
			array[num5] = new Coordinate(array[0]);
			ILinearRing shell = this.GeomFact.CreateLinearRing(array);
			IPolygon geom = this.GeomFact.CreatePolygon(shell, null);
			return (IPolygon)this.Rotate(geom);
		}

		public IPolygon CreateSquircle()
		{
			return this.CreateSupercircle(4.0);
		}

		public IPolygon CreateSupercircle(double power)
		{
			double y = 1.0 / power;
			double num = this._dim.MinSize / 2.0;
			Coordinate centre = this._dim.Centre;
			double num2 = System.Math.Pow(num, power);
			double num3 = num;
			double num4 = System.Math.Pow(num2 / 2.0, y);
			int num5 = this._nPts / 8;
			Coordinate[] array = new Coordinate[num5 * 8 + 1];
			double num6 = num4 / (double)num5;
			for (int i = 0; i <= num5; i++)
			{
				double num7 = 0.0;
				double num8 = num3;
				if (i != 0)
				{
					num7 = num6 * (double)i;
					double num9 = System.Math.Pow(num7, power);
					num8 = System.Math.Pow(num2 - num9, y);
				}
				array[i] = this.CreateCoordTrans(num7, num8, centre);
				array[2 * num5 - i] = this.CreateCoordTrans(num8, num7, centre);
				array[2 * num5 + i] = this.CreateCoordTrans(num8, -num7, centre);
				array[4 * num5 - i] = this.CreateCoordTrans(num7, -num8, centre);
				array[4 * num5 + i] = this.CreateCoordTrans(-num7, -num8, centre);
				array[6 * num5 - i] = this.CreateCoordTrans(-num8, -num7, centre);
				array[6 * num5 + i] = this.CreateCoordTrans(-num8, num7, centre);
				array[8 * num5 - i] = this.CreateCoordTrans(-num7, num8, centre);
			}
			array[array.Length - 1] = new Coordinate(array[0]);
			ILinearRing shell = this.GeomFact.CreateLinearRing(array);
			IPolygon geom = this.GeomFact.CreatePolygon(shell, null);
			return (IPolygon)this.Rotate(geom);
		}

		public ILineString CreateArc(double startAng, double angExtent)
		{
			Envelope envelope = this._dim.Envelope;
			double num = envelope.Width / 2.0;
			double num2 = envelope.Height / 2.0;
			double num3 = envelope.MinX + num;
			double num4 = envelope.MinY + num2;
			double num5 = angExtent;
			if (num5 <= 0.0 || num5 > System.Math.PI*2)
			{
				num5 = System.Math.PI*2;
			}
			double num6 = num5 / (double)(this._nPts - 1);
			Coordinate[] array = new Coordinate[this._nPts];
			int num7 = 0;
			for (int i = 0; i < this._nPts; i++)
			{
				double num8 = startAng + (double)i * num6;
				double x = num * Math.Cos(num8) + num3;
				double y = num2 * Math.Sin(num8) + num4;
				Coordinate coordinate = this.CreateCoord(x, y);
				array[num7++] = coordinate;
			}
			ILineString geom = this.GeomFact.CreateLineString(array);
			return (ILineString)this.Rotate(geom);
		}

		public IPolygon CreateArcPolygon(double startAng, double angExtent)
		{
			Envelope envelope = this._dim.Envelope;
			double num = envelope.Width / 2.0;
			double num2 = envelope.Height / 2.0;
			double num3 = envelope.MinX + num;
			double num4 = envelope.MinY + num2;
			double num5 = angExtent;
			if (num5 <= 0.0 || num5 > System.Math.PI*2)
			{
				num5 = System.Math.PI*2;
			}
			double num6 = num5 / (double)(this._nPts - 1);
			Coordinate[] array = new Coordinate[this._nPts + 2];
			int num7 = 0;
			array[num7++] = this.CreateCoord(num3, num4);
			for (int i = 0; i < this._nPts; i++)
			{
				double num8 = startAng + num6 * (double)i;
				double x = num * Math.Cos(num8) + num3;
				double y = num2 * Math.Sin(num8) + num4;
				array[num7++] = this.CreateCoord(x, y);
			}
			array[num7] = this.CreateCoord(num3, num4);
			ILinearRing shell = this.GeomFact.CreateLinearRing(array);
			IPolygon geom = this.GeomFact.CreatePolygon(shell, null);
			return (IPolygon)this.Rotate(geom);
		}

		protected IGeometryFactory GeomFact;

		protected IPrecisionModel PrecModel;

		private readonly GeometricShapeFactory.Dimensions _dim = new GeometricShapeFactory.Dimensions();

		private int _nPts = 100;

		private double _rotationAngle;

		protected class Dimensions
		{
			public Coordinate Base
			{
				get
				{
					return this._base;
				}
				set
				{
					this._base = value;
				}
			}

			public Coordinate Centre
			{
				get
				{
					if (this._centre == null)
					{
						this._centre = ((this.Base != null) ? new Coordinate(this.Base.X + this.Width * 0.5, this.Base.Y + this.Height * 0.5) : new Coordinate(0.0, 0.0));
					}
					return this._centre;
				}
				set
				{
					this._centre = value;
				}
			}

			public double Width
			{
				get
				{
					return this._width;
				}
				set
				{
					this._width = value;
				}
			}

			public double Height
			{
				get
				{
					return this._height;
				}
				set
				{
					this._height = value;
				}
			}

			public double Size
			{
				set
				{
					this.Height = value;
					this.Width = value;
				}
			}

			public double MinSize
			{
				get
				{
					return System.Math.Min(this.Width, this.Height);
				}
			}

			public Envelope Envelope
			{
				get
				{
					if (this.Base != null)
					{
						return new Envelope(this.Base.X, this.Base.X + this.Width, this.Base.Y, this.Base.Y + this.Height);
					}
					if (this.Centre != null)
					{
						return new Envelope(this.Centre.X - this.Width / 2.0, this.Centre.X + this.Width / 2.0, this.Centre.Y - this.Height / 2.0, this.Centre.Y + this.Height / 2.0);
					}
					return new Envelope(0.0, this.Width, 0.0, this.Height);
				}
				set
				{
					this._width = value.Width;
					this._height = value.Height;
					this._base = new Coordinate(value.MinX, value.MinY);
					this._centre = new Coordinate(value.Centre);
				}
			}

			private Coordinate _base;

			private Coordinate _centre;

			private double _width;

			private double _height;
		}
	}
}
